package net.sf.odinms.exttools.doclet.pages;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.PackageDoc;
import com.sun.javadoc.Tag;
import com.sun.tools.doclets.internal.toolkit.util.DirectoryManager;
import com.sun.tools.doclets.internal.toolkit.util.DocletConstants;
import com.sun.tools.doclets.internal.toolkit.util.Util;
import net.sf.odinms.exttools.doclet.AbstractPageWriter;
import net.sf.odinms.exttools.doclet.Doclet;

/**
 * Creates "{packagepath}/package-summary.html" which details a package.
 * Contains a package description (if provided) and a summary of all the
 * classes, interfaces, etc. in the package.
 */
public final class PackageSummary extends AbstractPageWriter {
	
	/** Localized header title strings, fetched once for optimization. */
	private static enum Summary {
		INTERFACE(CONF.propertyText("summary.Interface")), CLASS(
				CONF.propertyText("summary.Class")), ENUM(
				CONF.propertyText("summary.Enum")), EXCEPTION(
				CONF.propertyText("summary.Exception")), ERROR(
				CONF.propertyText("summary.Error")), ANNOTATION_TYPE(
				CONF.propertyText("summary.Annotation_Types"));
		
		/** String representing the package member type for headings. */
		private String headingTitle;
		
		/**
		 * Private constructor for enum elements.
		 * 
		 * @param headingTitle The localized value to store in an enum element.
		 */
		private Summary(String headingTitle) {
			this.headingTitle = headingTitle;
		}
		
		/**
		 * Method to easily fetch enum String values.
		 * 
		 * @return The title for a specific enum value.
		 */
		public String toString() {
			return headingTitle;
		}
	}
	
	/** The package for which the summary is to be generated. */
	private String pkgName;
	/** The previous package name in the alpha-order list. */
	private PackageDoc prev;
	/** The next package name in the alpha-order list. */
	private PackageDoc next;
	
	/** List of plain classes (if any) in the package being documented. */
	private ClassDoc[] classes;
	/** List of interfaces (if any) in the package being documented. */
	private ClassDoc[] interfaces;
	/** List of enums (if any) in the package being documented. */
	private ClassDoc[] enums;
	/** List of exceptions (if any) in the package being documented. */
	private ClassDoc[] exceptions;
	/** List of errors (if any) in the package being documented. */
	private ClassDoc[] errors;
	/** List of annotation types (if any) in the package being documented. */
	private ClassDoc[] annotationTypes;
	
	/**
	 * Constructs a PackageSummary object and uses it to create the package
	 * summary.
	 * 
	 * @param pkg Package for which a summary is to be generated.
	 * @param prev Previous package in the alpha-ordered list.
	 * @param next Next package in the alpha-ordered list.
	 */
	public static void generatePackageSummary(PackageDoc pkg, PackageDoc prev,
			PackageDoc next) {
		String path = DirectoryManager.getDirectoryPath(pkg);
		String filename = "package-summary" + CONF.ext;
		try {
			new PackageSummary(filename, path, pkg, prev, next);
			
			Util.copyDocFiles(CONF, Util.getPackageSourcePath(CONF, pkg),
					DirectoryManager.getDirectoryPath(pkg) + SLASH
							+ DocletConstants.DOC_FILES_DIR_NAME, true);
		}
		catch (IOException exc) {
			throw Doclet.exception(exc, filename);
		}
	}
	
	/**
	 * Create a page summarizing a given package, including members.
	 * 
	 * @param filename The desired name of the file (with extension).
	 * @param pathToFile The path from the document root to this file.
	 * @param pkg Package for which a summary is to be generated.
	 * @param prev The previous package in the alpha-ordered list.
	 * @param next The following package in the alpha-ordered list.
	 * @throws IOException If {@link java.io.FileOutputStream} creation fails.
	 */
	private PackageSummary(String filename, String pathToFile, PackageDoc pkg,
			PackageDoc prev, PackageDoc next) throws IOException {
		super(filename, pathToFile);
		pageType = PageType.PACKAGE;
		pkgName = pkg.name();
		CONF.currentPkg = pkg;
		this.prev = prev;
		this.next = next;
		
		// Construct lists of classes, interfaces, etc.
		interfaces =
				pkg.isIncluded()
						? pkg.interfaces()
						: CONF.classDocCatalog.interfaces(pkgName);
		classes =
				pkg.isIncluded()
						? pkg.ordinaryClasses()
						: CONF.classDocCatalog.ordinaryClasses(pkgName);
		enums =
				pkg.isIncluded()
						? pkg.enums()
						: CONF.classDocCatalog.enums(pkgName);
		exceptions =
				pkg.isIncluded()
						? pkg.exceptions()
						: CONF.classDocCatalog.exceptions(pkgName);
		errors =
				pkg.isIncluded()
						? pkg.errors()
						: CONF.classDocCatalog.errors(pkgName);
		annotationTypes =
				pkg.isIncluded()
						? pkg.annotationTypes()
						: CONF.classDocCatalog.annotationTypes(pkgName);
		
		windowTitle = pkg.name();
		
		String setFrameScript =
				"parent.packageFrame.location.href = 'package-frame" + CONF.ext
						+ "';";
		String[] metakeywords = {pkg.name() + " " + "package"};
		printXhtmlHeader(metakeywords, true, setFrameScript);
		
		printEntityCaption(pkg, CONF.propertyText("Package"));
		
		// Print first-line description and link to full description
		
		if (!CONF.nocomment && pkg.inlineTags().length > 0) {
			println(openDivWithID("Overview"));
			print(open("p"));
			print(getCommentSummary(pkg));
			println(open("span")
					+ " ("
					+ linkToLabelHref("See full package description",
							"#Package-Description") + ")" + close("span"));
			print(close("p"));
			println(close("div") + getComment("Overview"));
		}
		
		println(openDivWithID("Summaries"));
		printGroupSummary(interfaces, Summary.INTERFACE);
		printGroupSummary(classes, Summary.CLASS);
		printGroupSummary(enums, Summary.ENUM);
		printGroupSummary(exceptions, Summary.EXCEPTION);
		printGroupSummary(errors, Summary.ERROR);
		printGroupSummary(annotationTypes, Summary.ANNOTATION_TYPE);
		println(close("div") + getComment("Summaries"));
		
		if (pkg.inlineTags().length > 0) {
			println(openDivWithID("Package-Description"));
			println(open("h2") + CONF.propertyText("Description") + close("h2"));
			println(getCommentTagsString(pkg, pkg.inlineTags(), false, false));
			
			if (pkg.tags("author").length > 0) {
				for(Tag authorTag : pkg.tags("author")) {
					println(open("dt") + CONF.propertyText("detail.Author")  + close("dt"));
					println(open("dd") + authorTag.text() + close("dd"));
				}
			}
			if (pkg.tags("since").length > 0) {
				for(Tag sinceTag : pkg.tags("since")) {
					println(open("dt") + CONF.propertyText("detail.Since")  + close("dt"));
					println(open("dd") + sinceTag.text() + close("dd"));
				}
			}
			
			if (pkg.tags("version").length > 0) {
				for(Tag versionTag : pkg.tags("since")) {
					println(open("dt") + CONF.propertyText("detail.Version")  + close("dt"));
					println(open("dd") + versionTag.text() + close("dd"));
				}
			}

			println(close("div") + getComment("Package-Description"));
		}
		
		printXhtmlFooter();
		this.close();
	}
	
	/**
	 * Print the summary for a given group of member elements. (e.g. classes,
	 * interfaces, etc.)
	 * 
	 * @param groupElements The set of {@link ClassDoc} elements to summarize.
	 * @param label The label to display in the group heading.
	 */
	private void printGroupSummary(ClassDoc[] groupElements, Summary label) {
		if (groupElements != null && groupElements.length > 0) {
			Arrays.sort(groupElements);
			println(openDivWithID(Util.replaceText(label.headingTitle, " ", "-")));
			println(open("h2") + label + close("h2"));
			println(open("table"));
			for (ClassDoc classDoc : groupElements) {
				if (!Util.isCoreClass(classDoc)
						|| !CONF.isGeneratedDoc(classDoc)) {
					continue;
				}
				
				if (Util.isDeprecated(classDoc))
					println(open("tr class=\"deprecated\""));
				else
					println(open("tr"));
				println(open("th")
						+ linkToLabelHref(classDoc.name()
								+ typeParameters(classDoc), classDoc.name()
								+ CONF.ext) + close("th"));
				print(open("td"));

				if (Util.isDeprecated(classDoc)) {
					print(bold(CONF.propertyText("Deprecated")) + ". ");
					if (classDoc.tags("deprecated").length > 0) {
						print(getCommentTagsString(classDoc, classDoc.tags("deprecated"), false, true));
					}
				}
				print(getCommentSummary(classDoc));
				println(close("td"));
				println(close("tr"));
			}
			println(close("table"));
			println(close("div"));
		}
	}
	
	/** Highlight "Package" as current section, don't create link. */
	// TODO Determine behavior for 0+ packages, or classes not in a package
	protected void navLinkPackage() {
		println(listItemCurrent(PACKAGE));
	}
	
	/** Link "Use" to the use of the current package. */
	protected void navLinkUse() {
		println(listItem(linkToLabelHrefTitle(USE, "package-use" + CONF.ext,
				CONF.propertyText("navtitle.Use", pkgName))));
	}
	
	/** Link "Tree" to the tree for the current package. */
	protected void navLinkTree() {
		println(listItem(linkToLabelHrefTitle(TREE, "package-tree" + CONF.ext,
				CONF.propertyText("navtitle.Tree.package", pkgName))));
	}
	
	/** Print links to Summary sections for package elements. */
	protected void navWithinPage() {
		print(open("td id=\"WithinPage\""));
		// Links to summary sections within document
		List<String> summaries = new Vector<String>();
		if (interfaces.length > 0)
			summaries.add(linkToLabelHref(CONF.propertyText("Interfaces"),
					"#Interface-Summary"));
		if (classes.length > 0)
			summaries.add(linkToLabelHref(CONF.propertyText("Classes"),
					"#Class-Summary"));
		if (enums.length > 0)
			summaries.add(linkToLabelHref(CONF.propertyText("Enums"),
					"#Enum-Summary"));
		if (exceptions.length > 0)
			summaries.add(linkToLabelHref(CONF.propertyText("Exceptions"),
					"#Exception-Summary"));
		if (errors.length > 0)
			summaries.add(linkToLabelHref(CONF.propertyText("Errors"),
					"#Error-Summary"));
		if (annotationTypes.length > 0)
			summaries.add(linkToLabelHref(CONF.propertyText("AnnotationTypes"),
					"#Annotation-Type-Summary"));
		printUnorderedListWithLast(summaries);
		println(close("td"));
	}
	
	/** Print links to previous and next packages, or static label if none. */
	protected void navPreviousNext() {
		println(openULWithID("PreviousNext"));
		println(listItem(linkToLabelHref(PREV_PKG, hrefToDoc(prev)
				+ "package-summary" + CONF.ext))
				+ listItemLast(linkToLabelHref(NEXT_PKG, hrefToDoc(next)
						+ "package-summary" + CONF.ext)));
		
		println(close("ul"));
	}
}
